package com.fy.fyspace.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fy.fyspace.common.result.R;
import com.fy.fyspace.common.utils.StringTools;
import com.fy.fyspace.constant.KeysConstant;
import com.fy.fyspace.constant.ResultMessageConstant;
import com.fy.fyspace.ex.handler.ExHandler;
import com.fy.fyspace.mapper.FileInfoMapper;
import com.fy.fyspace.mapper.FileShareMapper;
import com.fy.fyspace.mapper.SpaceMapper;
import com.fy.fyspace.mapper.UserMapper;
import com.fy.fyspace.model.dto.file.DownLoadCodeDTO;
import com.fy.fyspace.model.dto.file.ShareSessionDTO;
import com.fy.fyspace.model.dto.file.ShowShareFileQueryDTO;
import com.fy.fyspace.model.entity.FileInfo;
import com.fy.fyspace.model.entity.FileShare;
import com.fy.fyspace.model.entity.Space;
import com.fy.fyspace.model.entity.User;
import com.fy.fyspace.model.enums.file.FileDelFlagEnums;
import com.fy.fyspace.model.enums.file.FileFolderTypeEnums;
import com.fy.fyspace.model.enums.space.SpaceStatusEnum;
import com.fy.fyspace.model.enums.space.SpaceTypeEnum;
import com.fy.fyspace.model.vo.file.FileShareVO;
import com.fy.fyspace.model.vo.file.FolderVO;
import com.fy.fyspace.model.vo.file.ShareFileVO;
import com.fy.fyspace.model.vo.user.UserSessionVO;
import com.fy.fyspace.service.ShowShareService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static cn.hutool.core.bean.BeanUtil.copyProperties;
import static com.fy.fyspace.common.result.R.ok;

@Slf4j
@Service
public class ShowShareServiceImpl implements ShowShareService {

    @Autowired
    private FileShareMapper fileShareMapper;

    @Autowired
    private SpaceMapper spaceMapper;

    @Autowired
    private FileInfoMapper fileInfoMapper;

    @Autowired
    private UserMapper userMapper;

    @Resource
    private RedisTemplate redisTemplate;

    // 添加日期格式化器
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 获取接收文件用户信息
     *
     * @param shareId
     * @return
     */
    @Override
    public R getShareInfo(String shareId, HttpSession httpSession) {
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);

        FileShare fileShare= fileShareMapper.selectById(shareId);
        ExHandler.throwIf(fileShare == null, ResultMessageConstant.SHARE_NOT_EXIST);

        User user = userMapper.selectById(fileShare.getUserId());
        ExHandler.throwIf(user == null, ResultMessageConstant.USER_NOT_FOUND);

        FileInfo fileInfo=fileInfoMapper.selectById(fileShare.getFileId());

        ShareFileVO shareFileVO = new ShareFileVO();

        shareFileVO.setShareTime(fileShare.getShareTime());
        shareFileVO.setAvatar(user.getUserAvatar());
        shareFileVO.setFolderType(fileInfo.getFolderType());
        shareFileVO.setExpireTime(fileShare.getExpireTime());
        shareFileVO.setFileId(fileShare.getFileId());
        shareFileVO.setNickName(user.getUserName());
        shareFileVO.setUserId(user.getId());

        if (fileInfo != null) {
            shareFileVO.setFileCover(fileInfo.getFileCover());
            shareFileVO.setFileName(fileInfo.getFileName());
            shareFileVO.setFileType(fileInfo.getFileType());
            shareFileVO.setFilePath(fileInfo.getFilePath());
        }

        shareFileVO.setIsLogin(userSessionVO != null);

        if (userSessionVO != null) {
            Long currentUserId = userSessionVO.getId();
            shareFileVO.setCurrentUser(Objects.equals(fileShare.getUserId(), currentUserId));
        } else {
            shareFileVO.setCurrentUser(false);
        }

        if (httpSession.getAttribute(KeysConstant.SHARE_CODE_SESSION) != null
                && Objects.equals(shareId, httpSession.getAttribute(KeysConstant.SHARE_CODE_SESSION))) {
            shareFileVO.setIsChecked(true);
        } else {
            shareFileVO.setIsChecked(false);
        }

        return ok(shareFileVO);
    }

    /**
     * 校验分享码
     *
     * @param shareId
     * @param code
     * @return
     */
    @Override
    public ShareSessionDTO checkShareCode(HttpSession httpSession, String shareId, String code) {
        FileShare fs = fileShareMapper.selectById(shareId);
        ExHandler.throwIf(fs == null, ResultMessageConstant.SHARE_NOT_EXIST);

        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        // 如果用户已登录，且是分享者本人，则不需要验证码
        if (userSessionVO != null && Objects.equals(fs.getUserId(), userSessionVO.getId())) {
            ShareSessionDTO shareSessionDTO = new ShareSessionDTO();
            shareSessionDTO.setShareId(shareId);
            httpSession.setAttribute(KeysConstant.SHARE_CODE_SESSION, shareId);
            return shareSessionDTO;
        }

        // 验证码校验
        ExHandler.throwIf(StrUtil.isBlank(code), ResultMessageConstant.CODE_NOT_NULL);
        ExHandler.throwIf(!code.equals(fs.getCode()), ResultMessageConstant.CODE_ERROR);

        ShareSessionDTO shareSessionDTO = new ShareSessionDTO();
        shareSessionDTO.setShareId(shareId);
        httpSession.setAttribute(KeysConstant.SHARE_CODE_SESSION, shareId);

        Integer showCount = fs.getShowCount();
        fs.setShowCount(showCount+1);
        fileShareMapper.updateById(fs);
        return shareSessionDTO;
    }

    /**
     * 提取分享文件
     *
     * @param fileQueryDTO
     * @return
     */
    @Override
    public R loadFileList(HttpSession httpSession, ShowShareFileQueryDTO fileQueryDTO) {
        String shareId = fileQueryDTO.getShareId();
        FileShare fileShare = fileShareMapper.selectById(shareId);
        log.info("获取文件列表的参数：{}", fileQueryDTO);
        ExHandler.throwIf(fileShare == null, ResultMessageConstant.SHARE_NOT_EXIST);

        // 校验分享是否过期
        Date expireTime = fileShare.getExpireTime();
        if (expireTime != null) {
            boolean isExpired = DateUtil.compare(expireTime, DateUtil.date()) <= 0;
            ExHandler.throwIf(isExpired, ResultMessageConstant.SHARE_EXPIRE_ERROR);
        }

        // 如果是文件夹，需要查询子文件
        FileInfo fileInfo = fileInfoMapper.selectById(fileShare.getFileId());

        ExHandler.throwIf(fileInfo == null,ResultMessageConstant.FILE_NOT_FOUND);

        FileShareVO fileShareVO=new FileShareVO();
        copyProperties(fileShare,fileShareVO);
        copyProperties(fileInfo,fileShareVO);

        return ok(fileShareVO);
    }

    /**
     * 获取发现文件夹目录信息
     *
     * @param path
     * @return
     */
    @Override
    public List<FolderVO> getFolderInfo(String path) {
        // 用/分割路径获取fileId
        String[] folderStr = path.split("/");

        List<FolderVO> foloderVOList = new ArrayList<>();

        // 遍历并设置返回参数
        for (String str : folderStr) {
            FolderVO foloderVO = new FolderVO();
            foloderVO.setFileName(fileInfoMapper.selectById(str).getFileName());
            foloderVO.setFileId(str);
            foloderVOList.add(foloderVO);
        }

        return foloderVOList;
    }

    /**
     * 获取文件路径
     *
     * @param fileId
     * @return
     */
    @Override
    public FileInfo getFilePath(String fileId) {
        return fileInfoMapper.selectById(fileId);
    }

    /**
     * 创建分享文件下载链接
     *
     * @param shareId
     * @param fileIds
     * @return
     */
    @Override
    public R createDownloadURL(HttpSession httpSession, String shareId, String fileIds) {
        // 生成随机下载码
        String downLoadCode = RandomUtil.randomNumbers(6);
        // 存入session
        httpSession.setAttribute(KeysConstant.SHARE_CODE_SESSION + shareId, downLoadCode);

        String[] split = fileIds.split(",");
        for (String fileId : split) {
            String redisDownLoadKey = KeysConstant.DOWNLOAD_KEY + shareId + downLoadCode + fileId;
            FileInfo fileInfo = fileInfoMapper.selectById(fileId);
            DownLoadCodeDTO dto = new DownLoadCodeDTO();
            dto.setFileName(fileInfo.getFileName());
            dto.setFilePath(fileInfo.getFilePath());
            redisTemplate.opsForValue().set(redisDownLoadKey, JSONUtil.toJsonStr(dto), 100, TimeUnit.SECONDS);
        }
        return ok();
    }

    /**保存分享文件
     *
     * @param userSessionVO
     * @param shareId
     * @param shareFileIds
     * @param myFolderId
     * @return
     */
    @Override
    @Transactional
    public R saveShare(UserSessionVO userSessionVO, String shareId, String shareFileIds, String myFolderId) {
        Long userId = userSessionVO.getId();
        String[] split = shareFileIds.split(",");
        long fileSize = 0; // 用来统计排除文件夹以外的，所有文件的大小总和，后续加入本地网盘的用户的使用空间信息中

        for (int i = 0; i < split.length; i++) {
            FileInfo fileInfo = fileInfoMapper.selectById(split[i]);

            if (Objects.equals(fileInfo.getFolderType(), FileFolderTypeEnums.FILE.getType())) {
                // 如果是文件
                fileSize = fileSize + copyFile(fileInfo, userSessionVO, myFolderId);
            } else if (Objects.equals(fileInfo.getFolderType(), FileFolderTypeEnums.FOLDER.getType())) {
                // 如果是文件夹
                fileSize = fileSize + copyFolder(fileInfo, userSessionVO, myFolderId);
            }
        }
        redisTemplate.delete(userSessionVO.getUserAccount() + "Space");

        // 更新用户的使用空间
        LambdaQueryWrapper<Space> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Space::getUserId, userId)
                .eq(Space::getIsDelete, SpaceStatusEnum.IS_NORMAL.getCode())
                .eq(Space::getSpaceType, SpaceTypeEnum.PERSONAL.getSpaceType());
        Space space = spaceMapper.selectList(wrapper).get(0);
        long newUseSpace = space.getTotalSize() + fileSize;
        ExHandler.throwIf(newUseSpace > space.getMaxSize(), ResultMessageConstant.INSUFFICIENT_SPACE);
        space.setTotalSize(newUseSpace);
        spaceMapper.updateById(space);
        return ok();
    }

    private long copyFile(FileInfo fileInfo, UserSessionVO userSessionVO, String myFolderId) {
        // 构建新文件名
        String codeEmailPart = StringTools.getEmailPrefix(userSessionVO.getUserAccount());
        // 构造新文件名称
        String newFileId = codeEmailPart + "ADD" + UUID.randomUUID().toString().substring(0, 6);

        long fileSize = fileInfo.getFileSize();
        fileInfo.setUserId(userSessionVO.getId());
        fileInfo.setFileId(newFileId);

        fileInfo.setFilePid(myFolderId);

        fileInfoMapper.insert(fileInfo);
        return fileSize;
    }

    private long copyFolder(FileInfo folderInfo, UserSessionVO userSessionVO, String myFolderId) {
        // 构造新文件夹名称
        String newFolderId = userSessionVO.getUserAccount() + "ADD" + UUID.randomUUID().toString().substring(0, 6);
        String sourceParentId = folderInfo.getFileId();
        folderInfo.setUserId(userSessionVO.getId());
        folderInfo.setFileId(newFolderId);

        folderInfo.setFilePid(myFolderId);

        fileInfoMapper.insert(folderInfo);

        // 递归处理子文件和子文件夹
        long fileSize = copyChildren(sourceParentId, newFolderId, userSessionVO);
        return fileSize;
    }

    private long copyChildren(String sourceParentId, String targetParentId, UserSessionVO userSessionVO) {
        long fileSize = 0L;
        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileInfo::getFilePid, sourceParentId);
        List<FileInfo> children = fileInfoMapper.selectList(wrapper);
        for (FileInfo child : children) {
            if (Objects.equals(child.getFolderType(), FileFolderTypeEnums.FILE.getType())) {
                // 如果是文件，直接复制
                fileSize = fileSize + copyFile(child, userSessionVO, targetParentId);
            } else if (Objects.equals(child.getFolderType(), FileFolderTypeEnums.FOLDER.getType())) {
                // 如果是文件夹，递归处理
                copyFolder(child, userSessionVO, targetParentId);
            }
        }
        return fileSize;
    }

    @Override
    public List<FileInfo> loadMyFolder(HttpSession httpSession, String filePid) {
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN);

        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileInfo::getFilePid, filePid)
                .eq(FileInfo::getDelFlag, FileDelFlagEnums.USING.getFlag())
                .eq(FileInfo::getUserId, userSessionVO.getId())
                .eq(FileInfo::getFolderType, FileFolderTypeEnums.FOLDER.getType());

        return fileInfoMapper.selectList(wrapper);
    }

    /**
     * 校验分享有效性
     *
     * @param shareId 分享ID
     * @return 校验结果
     */
    @Override
    public R getShareLoginInfo(String shareId) {
        FileShare fileShare = fileShareMapper.selectById(shareId);
        ExHandler.throwIf(fileShare == null, ResultMessageConstant.SHARE_NOT_EXIST);

        FileInfo fileInfo = fileInfoMapper.selectById(fileShare.getFileId());
        ExHandler.throwIf(fileInfo == null, ResultMessageConstant.FILE_NOT_FOUND);

        // 检查文件状态
        ExHandler.throwIf(FileDelFlagEnums.RECYCLE.getDesc().equals(fileInfo.getDelFlag())
            || FileDelFlagEnums.DEL.getFlag().equals(fileInfo.getDelFlag()),
            ResultMessageConstant.SHARE_FILE_EXPIRED);

        // 检查过期时间
        Date expireTime = fileShare.getExpireTime();
        if (expireTime != null) {
            boolean isExpired = DateUtil.compare(expireTime, DateUtil.date()) <= 0;
            ExHandler.throwIf(isExpired, ResultMessageConstant.SHARE_EXPIRE_ERROR);
        }

        return ok();
    }
}
